package com.xy.service.system;

import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.xy.api.constant.RedisConstant;
import com.xy.api.exception.BizException;
import com.xy.api.model.dto.system.UserInfoDTO;
import com.xy.api.model.request.system.LoginRequest;
import com.xy.api.model.request.system.KeywordPageRequest;
import com.xy.api.model.response.Rows;
import com.xy.api.service.system.IUserService;
import com.xy.auth.UserContext;
import com.xy.mapper.system.RoleMapper;
import com.xy.mapper.system.RoleMenuMapper;
import com.xy.mapper.system.UserInfoMapper;
import com.xy.mapper.system.UserRoleMapper;
import com.xy.model.condition.system.UserInfoCondition;
import com.xy.model.po.system.RolePO;
import com.xy.model.po.system.UserInfoPO;
import com.xy.model.po.system.UserRolePO;
import com.xy.utils.CustomBeanUtil;
import com.xy.utils.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.time.Duration;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
public class UserServiceImpl implements IUserService {

    @Autowired
    private UserInfoMapper userInfoMapper;

    @Autowired
    private UserRoleMapper userRoleMapper;

    @Autowired
    private RoleMapper roleMapper;

    @Autowired
    private RoleMenuMapper roleUrlMapper;

    @Autowired
    private RedisUtil redisUtil;

    @Override
    public UserInfoDTO login(LoginRequest request) {
        Assert.hasText(request.getPhone(), "手机号必填");
        Assert.hasText(request.getPassword(), "密码必填");

        UserInfoPO userInfoPO = userInfoMapper.findByPhone(request.getPhone());
        if (null == userInfoPO || !Objects.equals(userInfoPO.getPassword(), request.getPassword())) {
            throw new BizException("用户密码错误");
        }
        UserInfoDTO userInfo = new UserInfoDTO();
        CustomBeanUtil.copyProperties(userInfoPO, userInfo);
        fillUserRoleCode(userInfo);

        String token = UUID.randomUUID().toString();
        userInfo.setToken(token);
        String key = String.format(RedisConstant.TOKEN, token);
        redisUtil.set(key, JSONObject.toJSONString(userInfo), Duration.ofHours(2));
        return userInfo;
    }

    @Override
    public UserInfoDTO getUserInfo() {
        UserInfoDTO currentUserInfo = UserContext.get();
        return currentUserInfo;
    }

    /**
     * 填充用户角色code
     *
     * @param userInfo
     */
    private void fillUserRoleCode(UserInfoDTO userInfo) {
        if (null == userInfo) {
            return;
        }
        Long userId = userInfo.getId();
        List<RolePO> roleList = roleMapper.findByUserId(userId);
        if (!CollectionUtils.isEmpty(roleList)) {
            List<String> roleCodeList = roleList.stream().map(e -> e.getRoleCode()).collect(Collectors.toList());
            userInfo.setRoleCodeList(roleCodeList);
        }
    }

    @Override
    public void logout(UserInfoDTO request) {
        UserInfoDTO currentUserInfo = UserContext.get();
        String token = currentUserInfo.getToken();
        String key = String.format(RedisConstant.TOKEN, token);
        redisUtil.delete(key);
    }

    @Override
    public Rows<UserInfoDTO> queryUserList(KeywordPageRequest request) {
        UserInfoCondition userInfoCondition = new UserInfoCondition();
        userInfoCondition.setKeyword(request.getKeyword());
        Integer total = userInfoMapper.countList(userInfoCondition);
        if (total == 0) {
            return new Rows<>(null, 0);
        }
        Integer pageSize = null != request.getPageSize() ? request.getPageSize() : 10;
        Integer pageNum = null != request.getPageNum() ? request.getPageNum() : 1;
        Integer pageStart = (pageNum - 1) * pageSize;
        userInfoCondition.setPageSize(pageSize);
        userInfoCondition.setPageStart(pageStart);
        List<UserInfoPO> userInfoPOS = userInfoMapper.findList(userInfoCondition);
        List<UserInfoDTO> list = CustomBeanUtil.copyListProperties(userInfoPOS, UserInfoDTO::new);

        fillUserRoleCodeList(list);
        Rows<UserInfoDTO> result = new Rows<>();
        result.setTotal(total);
        result.setRows(list);
        return result;
    }

    @Override
    public UserInfoDTO queryUserDetail(Long userId) {
        Assert.notNull(userId, "用户ID必填");
        UserInfoPO userInfoPO = userInfoMapper.selectById(userId);
        UserInfoDTO userInfo = new UserInfoDTO();
        CustomBeanUtil.copyProperties(userInfoPO, userInfo);

        //角色
        fillUserRoleCodeList(userInfo);
        return userInfo;
    }

    /**
     * 填充用户关联的角色
     *
     * @param userInfo
     */
    private void fillUserRoleCodeList(UserInfoDTO userInfo) {
        if (null == userInfo) {
            return;
        }
        LambdaQueryWrapper<UserRolePO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(UserRolePO::getUserId, userInfo.getId()).eq(UserRolePO::getIsDeleted, false);
        List<UserRolePO> userRolePOS = userRoleMapper.selectList(queryWrapper);
        if (!CollectionUtils.isEmpty(userRolePOS)) {
            List<String> roleCodeList = userRolePOS.stream().map(e -> e.getRoleCode()).distinct().collect(Collectors.toList());
            userInfo.setRoleCodeList(roleCodeList);
        }
    }

    /**
     * 填充用户关联的角色
     *
     * @param list
     */
    private void fillUserRoleCodeList(List<UserInfoDTO> list) {
        if (CollectionUtils.isEmpty(list)) {
            return;
        }
        List<Long> userIdList = list.stream().map(e -> e.getId()).collect(Collectors.toList());
        LambdaQueryWrapper<UserRolePO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.in(UserRolePO::getUserId, userIdList).eq(UserRolePO::getIsDeleted, false);
        List<UserRolePO> userRolePOS = userRoleMapper.selectList(queryWrapper);
        if (!CollectionUtils.isEmpty(userRolePOS)) {
            Map<Long, List<String>> userRoleCodeListMap = userRolePOS.stream().collect(Collectors.groupingBy(UserRolePO::getUserId, Collectors.mapping(UserRolePO::getRoleCode, Collectors.toList())));
            for (UserInfoDTO userInfo : list) {
                if (userRoleCodeListMap.containsKey(userInfo.getId())) {
                    List<String> roleCodeList = userRoleCodeListMap.get(userInfo.getId());
                    userInfo.setRoleCodeList(roleCodeList);
                }
            }
        }
    }

    @Override
    public void editUser(UserInfoDTO userInfo) {
        Assert.hasText(userInfo.getName(), "用户名必填");
        Assert.hasText(userInfo.getPhone(), "手机号必填");
        Assert.notNull(userInfo.getPassword(), "密码必填");
        UserInfoPO userByPhone = userInfoMapper.findByPhone(userInfo.getPhone());
        if (null != userByPhone) {
            if (null == userInfo.getId()) {
                throw new BizException("手机号已存在！");
            } else if (!Objects.equals(userInfo.getId(), userByPhone.getId())) {
                throw new BizException("手机号已存在！");
            }
        }
        UserInfoPO record = new UserInfoPO();
        CustomBeanUtil.copyProperties(userInfo, record);
        if (null == userInfo.getId()) {
            userInfoMapper.insert(record);

        } else {
            userInfoMapper.updateById(record);

            LambdaQueryWrapper<UserRolePO> deleteWrapper = new LambdaQueryWrapper<>();
            deleteWrapper.eq(UserRolePO::getUserId, record.getId());
            UserRolePO deleteRecord = new UserRolePO();
            deleteRecord.setIsDeleted(true);
            userRoleMapper.update(deleteRecord, deleteWrapper);
        }
        insertUserRole(userInfo.getRoleCodeList(), record.getId());
    }

    @Transactional
    private void insertUserRole(List<String> roleCodeList, Long userId) {
        if (CollectionUtils.isEmpty(roleCodeList)) {
            return;
        }
        for (String roleCode : roleCodeList) {
            UserRolePO userRoleRecord = new UserRolePO();
            userRoleRecord.setRoleCode(roleCode);
            userRoleRecord.setUserId(userId);
            userRoleMapper.insert(userRoleRecord);
        }
    }

    @Override
    public void deleteUser(Long userId) {
        UserInfoPO record = new UserInfoPO();
        record.setId(userId);
        record.setIsDeleted(true);
        userInfoMapper.updateById(record);
    }
}
